以下内容均基于 16.8.6 版本。
setState
是同步还是异步,这个问题很多人讨论过,各种说法都有,面试时也经常会问到,那它到底是同步还是异步呢?
我认为,它既是同步的,也是异步的。
首先来说说我们是怎么判断它是同步还是异步,往往是在 setState
后访问 state
来确定,所以下面这个例子很常见
1 | class App extends React.Component { |
第一种情况两次打印结果都是 0;第二种分别打印 1 和 2;
批量更新所以看起来像异步实际还是同步
大部分博客就会提到「批量更新」这个概念,可以理解为在一次同步任务内,setState
会「合并」到一起进行更新,但是 setTimeout
不在同步任务内,所以不进行「合并」,而是直接依次执行(或者说依次更新)。
用伪代码来说明是这样的
1 | let isBatchingUpdates = false; |
可以发现效果和最开始的例子是相同的。
单纯从代码来说,这应该是同步的,因为没有 setTimeout
或者其他什么异步方法介入。那么是否表示,setState
就是同步的呢?
并不是,代码可以有多个条件分支,那为什么不能在「某种情况」下使用 setTimeout
呢?
使用 ConcurrentMode 时 setState 是异步
如果把 App
组件用 React.unstable_ConcurrentMode
组件包裹,即使在 setTimeout
内调用,两次打印的结果还是 0 和 0,即和第一种情况的结果一致。
这是因为触发了异步逻辑,继续用上面伪代码来说明会是这样的
1 | // 省略相同的代码 |
当然实际代码要复杂得多,并且 scheduleCallback
的实现其实是在 scheduler
包内的。
https://github.com/facebook/react/blob/master/packages/scheduler/src/Scheduler.js#L295
结论
所以,setState
是同步还是异步的呢?我认为
它既是同步也是异步,视情况而定。